package brandescm.bluetooth232;

import java.util.ArrayList;



public class ModbusRTUMaster 
{
	public ModbusRTUMaster()
	{
		
	}
	
	public byte dMODBUS_RTU_MASTER_BUFFER_SIZE = 125;// dUART_0_BUFFER_SIZE

	enum eState
	{
	  eSTATE_IDLE,
	  eSTATE_TRANSMITTING,
	  eSTATE_WAITING_FOR_RESPONSE,
	  eSTATE_RESPONSE_RECIEVED,
	  eSTATE_RESPONSE_PACKET_ERROR
	};

	enum eResponse
	{
	  eRESPONSE_VALID,
	  eRESPONSE_NOT_VALID
	};
	
	enum eProtocolState
	{
		ePROTOCOL_STATE_IDLE,
		ePROTOCOL_STATE_BUSY,
		ePROTOCOL_STATE_PACKET_ERROR
	};
	
	enum eMasterError
	{
	  eMODBUS_RTU_PACKET_TOO_SMALL_ERROR,
	  eMODBUS_RTU_WRONG_DEVICE_ADDRESS_ERROR,
	  eMODBUS_RTU_COMMAND_DOESNT_MATCH_ERROR,
	  eMODBUS_RTU_CRC_ERROR,
	  eMODBUS_RTU_EXCEPTION_ILLEGAL_COMMAND_ERROR,
	  eMODBUS_RTU_EXCEPTION_ILLEGAL_ADDRESS_ERROR,
	  eMODBUS_RTU_EXCEPTION_ILLEGAL_VALUE_ERROR,
	  eMODBUS_RTU_PACKET_LENGTH_NOT_CORRECT_ERROR
	};

	// --------------------------------
	// Module Scope Function Prototypes
	// --------------------------------
	//void _ModbusRtuMaster_Callback(tPORT Port, tUBYTE Bytes);
	//_ModbusRtuMaster_ValidateResponse();
	//tUWORD _ModbusRtuMaster_CalculateCrc(tUBYTE *Buffer, tWORD Size);

	// ----------------------
	// Module Scope Variables
	// ----------------------
	String Error;
	eState ModbusRtuMasterState;
	ArrayList<Short> ModbusRtuMasterPacket = new ArrayList<Short>();
	int ModbusRtuMasterPacketCount;
	short ModbusRtuMasterDeviceAddress;
	short ModbusRtuMasterCommand;
	// ----------------------
	// Public Scope Functions
	// ----------------------
	public String getError()
	{
		return Error;
	}
	// ==================================================================
	// FUNCTION:	  ModbusRtuMaster_Init
	//
	// DESCRIPTION: This function initializes the Modbus Rtu comms.
	//
	// INPUTS:		  None
	// 
	// OUTPUTS:		  None
	// 
	// RETURNS:		  None
	// ------------------------------------------------------------------
	//public void ModbusRtuMaster_Init(tPORT_SETTINGS *Port)
	public void ModbusRtuMaster_Init()
	{
	  ModbusRtuMasterState = eState.eSTATE_IDLE;
	//  if (Port->Parity == eENUM_GATEWAY_PARITY_NONE)      Port->Parity = dHAL_UART_NO_PARITY;
	//  else if (Port->Parity == eENUM_GATEWAY_PARITY_EVEN) Port->Parity = dHAL_UART_EVEN_PARITY;
	//  else if (Port->Parity == eENUM_GATEWAY_PARITY_ODD)  Port->Parity = dHAL_UART_ODD_PARITY;
	//  else                                                Port->Parity = dHAL_UART_NO_PARITY;

	  //if (Port->BaudRate == 19200)     Port->BaudRate = dHAL_UART_BAUD_19200;
	  //else if (Port->BaudRate == 9600) Port->BaudRate = dHAL_UART_BAUD_9600;
	  //else                             Port->BaudRate = dHAL_UART_BAUD_38400;

	  //HalUart_Open(eLOCAL_BUS_COMMS_PORT);
	  //HalUart_SetProperty(eLOCAL_BUS_COMMS_PORT, dHAL_UART_BAUDRATE, Port->BaudRate); 
	//  HalUart_SetProperty(eLOCAL_BUS_COMMS_PORT, dHAL_UART_MEDIA, dHAL_UART_485_2WIRE); 
	//  HalUart_SetProperty(eLOCAL_BUS_COMMS_PORT, dHAL_UART_DUPLEX, dHAL_UART_HALF_DUPLEX); 
	//  HalUart_SetProperty(eLOCAL_BUS_COMMS_PORT, dHAL_UART_DATABITS, Port->DataBits); 
	//  HalUart_SetProperty(eLOCAL_BUS_COMMS_PORT, dHAL_UART_PARITY, Port->Parity); 
	  //HalUart_SetProperty(eLOCAL_BUS_COMMS_PORT, dHAL_UART_TIMEOUT, 2); //2 milliseconds for 19200 baud rate.
	  //HalUart_RegisterTimeoutCallback(eLOCAL_BUS_COMMS_PORT, _ModbusRtuMaster_Callback);
	}

	// ==================================================================
	// FUNCTION:	  ModbusRtuMaster_Tick
	//
	// DESCRIPTION: This function manages the Modbus Rtu comms transactions.
	//
	// INPUTS:		  None
	// 
	// OUTPUTS:		  None
	// 
	// RETURNS:		  None
	// ------------------------------------------------------------------
	public eProtocolState ModbusRtuMaster_Tick()
	{
	  eProtocolState Error;

	  if (ModbusRtuMasterState == eState.eSTATE_IDLE)
	  {
	    Error = eProtocolState.ePROTOCOL_STATE_IDLE; //nothing to do.
	  }
	  else if (ModbusRtuMasterState == eState.eSTATE_TRANSMITTING)
	  {
	    Error = eProtocolState.ePROTOCOL_STATE_BUSY;
	    //if (HalUart_GetState(eLOCAL_BUS_COMMS_PORT) == eHAL_UART_RECIEVE_ENABLED)
	    //{
	      if (ModbusRtuMasterDeviceAddress == 0) //broadcast message - no response expected.
	      {
	        ModbusRtuMasterState = eState.eSTATE_IDLE;
	        Error = eProtocolState.ePROTOCOL_STATE_IDLE;
	      }
	      else
	      {
	        ModbusRtuMasterState = eState.eSTATE_WAITING_FOR_RESPONSE;
	      }
	    //}
	  }
	  else if (ModbusRtuMasterState == eState.eSTATE_WAITING_FOR_RESPONSE)
	  {
	    Error = eProtocolState.ePROTOCOL_STATE_BUSY;
	  }
	  else if (ModbusRtuMasterState == eState.eSTATE_RESPONSE_RECIEVED)
	  {
	    if (_ModbusRtuMaster_ValidateResponse() == eResponse.eRESPONSE_VALID)
	    {
	      ModbusRtuMasterState = eState.eSTATE_IDLE;
	      Error = eProtocolState.ePROTOCOL_STATE_IDLE;
	    }
	    else
	    {
	      ModbusRtuMasterState = eState.eSTATE_RESPONSE_PACKET_ERROR;
	      Error = eProtocolState.ePROTOCOL_STATE_PACKET_ERROR;
	    }
	  }
	  else if (ModbusRtuMasterState == eState.eSTATE_RESPONSE_PACKET_ERROR)
	  {
	    Error = eProtocolState.ePROTOCOL_STATE_PACKET_ERROR;
	  }
	  else //shouldn't get here.
	  {
	    ModbusRtuMasterState = eState.eSTATE_IDLE;
	    Error = eProtocolState.ePROTOCOL_STATE_IDLE;
	  }
	  return(Error);
	}

	// ==================================================================
	// FUNCTION:	  _ModbusRtuMaster_Callback
	//
	// DESCRIPTION: This function handles a packet character timeout.
	//
	// INPUTS:		  None
	// 
	// OUTPUTS:		  None
	// 
	// RETURNS:		  None
	// ------------------------------------------------------------------
	//public void _ModbusRtuMaster_Callback(tPORT Port, int Bytes)
	//{
	  //if (Port != eLOCAL_BUS_COMMS_PORT) return;
	  //if (Bytes > 0)
	  //{
//	    ModbusRtuMasterPacketCount = HalUart_GetBlock(eLOCAL_BUS_COMMS_PORT, ModbusRtuMasterPacket, dMODBUS_RTU_MASTER_BUFFER_SIZE);
//	    ModbusRtuMasterState = eState.eSTATE_RESPONSE_RECIEVED;
//	  }
//	  else
//	    ModbusRtuMasterState = eState.eSTATE_RESPONSE_PACKET_ERROR;
//	}

	// ==================================================================
	// FUNCTION:	  ModbusRtuMaster_Read
	//
	// DESCRIPTION: This function starts a Modbus Rtu read transaction.
	//
	// INPUTS:		  None
	// 
	// OUTPUTS:		  None
	// 
	// RETURNS:		  None
	// ------------------------------------------------------------------
	void ModbusRtuMaster_Read(short DeviceAddress, short StartRegister, short NumberOfRegisters)
	{
	  int Crc;

	  if (DeviceAddress > 247) return;

	  ModbusRtuMasterDeviceAddress = (short)(DeviceAddress & 0x00FF);
	  ModbusRtuMasterCommand = 0x03;

	  ModbusRtuMasterPacket.add(0, (short)ModbusRtuMasterDeviceAddress);
	  ModbusRtuMasterPacket.add(1, (short)ModbusRtuMasterCommand);

	  ModbusRtuMasterPacket.add(2,(short)(StartRegister >> 8));
	  ModbusRtuMasterPacket.add(3,(short)(StartRegister & 0x00FF));
	  ModbusRtuMasterPacket.add(4,(short)(NumberOfRegisters >> 8));
	  ModbusRtuMasterPacket.add(5,(short)(NumberOfRegisters & 0x00FF));

	  Crc = _ModbusRtuMaster_CalculateCrc(ModbusRtuMasterPacket, 6);
	  ModbusRtuMasterPacket.add(6,((short)(Crc & 0x00FF)));//Crc.bytes.low_byte;
	  ModbusRtuMasterPacket.add(7,(short)((Crc & 0xFF00) >> 8));//Crc.bytes.high_byte;
	  ModbusRtuMasterPacketCount = 8;

//	  if (HalUart_PutBlock(eLOCAL_BUS_COMMS_PORT, ModbusRtuMasterPacket, ModbusRtuMasterPacketCount) == eNO_ERROR)
//	  {
//	    ModbusRtuMasterState = eState.eSTATE_TRANSMITTING;
//	  }
	}

	// ==================================================================
	// FUNCTION:	  ModbusRtuMaster_Write
	//
	// DESCRIPTION: This function starts a Modbus Rtu write transaction.
	//
	// INPUTS:		  None
	// 
	// OUTPUTS:		  None
	// 
	// RETURNS:		  None
	// ------------------------------------------------------------------
	public ArrayList<Short> ModbusRtuMaster_Write(short DeviceAddress, short Register, 
					short NumberOfRegisters, short Value)
	{
	  int Crc;
	  short Index;
	  //byte PtrValue;
	  int NumberOfBytes;

	  if (DeviceAddress > 247) 
		  return ModbusRtuMasterPacket;

	  ModbusRtuMasterDeviceAddress = DeviceAddress;
	  ModbusRtuMasterCommand = 0x10;

	  Index = 0;
	  ModbusRtuMasterPacket.add(Index++, (short)ModbusRtuMasterDeviceAddress);
	  ModbusRtuMasterPacket.add(Index++, (short)ModbusRtuMasterCommand);

	  ModbusRtuMasterPacket.add(Index++, (short)(Register >> 8));
	  ModbusRtuMasterPacket.add(Index++, (short)(Register & 0x00FF));

	  ModbusRtuMasterPacket.add(Index++, (short)0x00);
	  ModbusRtuMasterPacket.add(Index++, (short)(NumberOfRegisters & 0x00FF));

	  NumberOfBytes = (NumberOfRegisters * 2);
	  ModbusRtuMasterPacket.add(Index++, (short)NumberOfBytes);

	  ModbusRtuMasterPacket.add(Index++, (short)(Value >> 8));
	  ModbusRtuMasterPacket.add(Index++, (short)(Value & 0x00FF));
//	  PtrValue = Value;
//	  while (Index < (NumberOfBytes + 7))
//	  {
//	    ModbusRtuMasterPacket[Index] = PtrValue;
//	    PtrValue++;
//	    Index++;
//	  }

	  Crc = _ModbusRtuMaster_CalculateCrc(ModbusRtuMasterPacket, Index);
	  ModbusRtuMasterPacket.add(Index++, (short)((Crc & 0xFF00) >> 8));//Crc.bytes.low_byte;
	  ModbusRtuMasterPacket.add(Index++, (short)(Crc & 0x00FF));//Crc.bytes.high_byte;
	  ModbusRtuMasterPacketCount = Index;
	  
	  return ModbusRtuMasterPacket;
	  //if (HalUart_PutBlock(eLOCAL_BUS_COMMS_PORT, ModbusRtuMasterPacket, ModbusRtuMasterPacketCount) == eNO_ERROR)
	  //{
	    //ModbusRtuMasterState = eSTATE_TRANSMITTING;
	  //}
	}

	// ==================================================================
	// FUNCTION:	  ModbusRtuMaster_ValidateResponse
	//
	// DESCRIPTION: This function validates a Modbus Rtu response.
	//
	// INPUTS:		  None
	// 
	// OUTPUTS:		  None
	// 
	// RETURNS:		  None
	// ------------------------------------------------------------------
	public eResponse _ModbusRtuMaster_ValidateResponse()
	{
	  
	  eResponse error;
	  //int NumberOfBytes;

	  error = eResponse.eRESPONSE_NOT_VALID;
	  if (ModbusRtuMasterPacketCount < 5)
	  {
	    Error = eMasterError.eMODBUS_RTU_PACKET_TOO_SMALL_ERROR.toString();
	  }
	  else if (ModbusRtuMasterPacket.get(0) != ModbusRtuMasterDeviceAddress)
	  {
	    Error = eMasterError.eMODBUS_RTU_WRONG_DEVICE_ADDRESS_ERROR.toString();
	  }
	  else if ((ModbusRtuMasterPacket.get(1) & 0x7F) != ModbusRtuMasterCommand)
	  {
	    Error = eMasterError.eMODBUS_RTU_COMMAND_DOESNT_MATCH_ERROR.toString();
	  }
	  else if (_ModbusRtuMaster_CalculateCrc(ModbusRtuMasterPacket, ModbusRtuMasterPacketCount) != 0)
	  {
	    Error = eMasterError.eMODBUS_RTU_CRC_ERROR.toString();
	  }
	  else if ((ModbusRtuMasterPacket.get(1) & 0x80) == 0x80)
	  {
	    if (ModbusRtuMasterPacket.get(2) == 0x02)
	    {
	      Error = eMasterError.eMODBUS_RTU_EXCEPTION_ILLEGAL_COMMAND_ERROR.toString();
	    }
	    else if (ModbusRtuMasterPacket.get(2) == 0x03)
	    {
	      Error = eMasterError.eMODBUS_RTU_EXCEPTION_ILLEGAL_ADDRESS_ERROR.toString();
	    }
	    else //if (ModbusRtuMasterPacket[2] == 0x01)
	    {
	      Error = eMasterError.eMODBUS_RTU_EXCEPTION_ILLEGAL_VALUE_ERROR.toString();
	    }
	  }
	  else if (ModbusRtuMasterPacket.get(1) == 0x03)
	  {
	    //NumberOfBytes = (int)ModbusRtuMasterPacket[2];
	    //GatewayManager_StoreSerialPoint((tUBYTE *)&ModbusRtuMasterPacket[3], NumberOfBytes);
	    error = eResponse.eRESPONSE_VALID;
	  }
	  else if (ModbusRtuMasterPacket.get(1) == 0x10)
	  {
	    if (ModbusRtuMasterPacketCount == 8)
	    {
	      //call so the 'write count' can be incremented.
	      //NumberOfBytes = 0;
	      //GatewayManager_StoreSerialPoint((tUBYTE *)&ModbusRtuMasterPacket[3], NumberOfBytes);
	      error = eResponse.eRESPONSE_VALID;
	    }
	    else
	    {
	      Error = eMasterError.eMODBUS_RTU_PACKET_LENGTH_NOT_CORRECT_ERROR.toString();
	    }
	  }
	  return(error);
	}

	// ==================================================================
	// FUNCTION:	  _ModbusRtuMaster_CalculateCrc
	//
	// DESCRIPTION: This function calculates the CRC for a RTU packet.
	//
	// INPUTS:		  None
	// 
	// OUTPUTS:		  None
	// 
	// RETURNS:		  None
	// ------------------------------------------------------------------
	public int _ModbusRtuMaster_CalculateCrc(ArrayList<Short> Buffer, int Size)
	{
	  int ByteIndex;
	  int BitIndex;
	  int CrcValue;

	  CrcValue = 0xFFFF;

	  for (ByteIndex = 0; ByteIndex < Size; ByteIndex++)
	  {
	    CrcValue ^= Buffer.get(ByteIndex);
	    for (BitIndex = 0; BitIndex < 8; BitIndex++)
	      {
	      if ((CrcValue & 0x01) == 1)
	      {
	        CrcValue >>= 1;
	        CrcValue ^= 0xA001;
	      }
	      else
	      { 
	        CrcValue >>= 1; 
	      }
	    }
	  }
	  return(CrcValue);
	}
	
	
}
